﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Devices;
using System.Windows.Threading;
using System.Windows.Media.Imaging;
using ZXing;
using System.ComponentModel;
using System.IO.IsolatedStorage;

namespace CameraBanking
{
    public enum BarcodeType { Barcode, Qrcode }

    public partial class Camera : PhoneApplicationPage
    {
        private PhotoCamera _PhotoCamera;
        private bool _podeVoltar;
        private bool _barraVisivel;
        private BarcodeReader _BarcodeReader;
        private DispatcherTimer _Timer;
        private WriteableBitmap _WriteableBitmapCropped;
        private int _tryTimes;
        private bool _HasBarcodeIconButton { get; set; }
        private ApplicationBarIconButton _QrCode = new ApplicationBarIconButton();
        private ApplicationBarIconButton _BarCode = new ApplicationBarIconButton();
        private double resolutionWidth;

        public BarcodeType BarcodeType { get; set; }

        public Boolean OverrideAppBar { get; set; }

        /// <summary>
        /// Indica o estado da miniatura, ou seja, se a mesma está ou não aberta.
        /// </summary>
        public bool ExibirHelper
        {
            get
            {
                bool estadoAtualDaMiniatura = RecuperarEstadoDaMiniatura();
                if (estadoAtualDaMiniatura)
                    return true;
                else
                    return false;
            }
            set
            {
                if (value)
                {
                    GrayImage.Visibility = Visibility.Visible;
                }
                else
                {
                    GrayImage.Visibility = Visibility.Collapsed;
                }
                GravarEstadoDaMiniatura(value);
            }
        }

        public Camera()
        {
            InitializeComponent();

            _QrCode = new ApplicationBarIconButton(new Uri("/Assets/appbar.qr.png", UriKind.Relative));
            _QrCode.Text = "qr code";
            _QrCode.Click += QrCodeOrBarCode_Click;

            _BarCode = new ApplicationBarIconButton(new Uri("/Assets/appbar.barcode.png", UriKind.Relative));
            _BarCode.Text = "código de barras";
            _BarCode.Click += QrCodeOrBarCode_Click;


            if (ExibirHelper)
                GrayImage.Visibility = Visibility.Visible;
            else
                GrayImage.Visibility = Visibility.Collapsed;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            SystemTray.IsVisible = false;

            if (ApplicationBar.IsVisible)
            {
                _barraVisivel = true;
                ApplicationBar.IsVisible = false;
            }

            QrCodeOrBarCode();
            
            //InitializePhotoCamera();
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);

            if (_Timer != null)
            {
                _Timer.Stop();
                _Timer.Tick -= TryToFocusOrCaptureImage;
            }

            if (_BarcodeReader != null)
            {
                _BarcodeReader.ResultFound -= _BarcodeReader_ResultFound;
            }

            if (_PhotoCamera != null)
            {
                _PhotoCamera.Initialized -= _PhotoCamera_Initialized;
                if (_PhotoCamera.IsFocusSupported)
                    _PhotoCamera.AutoFocusCompleted -= _PhotoCamera_AutoFocusCompleted;
                _PhotoCamera.CaptureImageAvailable -= _PhotoCamera_CaptureImageAvailable;
                _PhotoCamera.CaptureCompleted -= _PhotoCamera_CaptureCompleted;

                _PhotoCamera.Dispose();

                _PhotoCamera = null;

                System.Diagnostics.Debug.WriteLine("Camera disposed!");
            }                
        }

        private void InitializePhotoCamera()
        {
            if (_Timer != null)
            {
                _Timer.Stop();
                _Timer.Tick -= TryToFocusOrCaptureImage;
            }

            if (_PhotoCamera != null)
            {
                _PhotoCamera.Initialized -= _PhotoCamera_Initialized;
                if (_PhotoCamera.IsFocusSupported)
                    _PhotoCamera.AutoFocusCompleted -= _PhotoCamera_AutoFocusCompleted;
                _PhotoCamera.CaptureImageAvailable -= _PhotoCamera_CaptureImageAvailable;
                _PhotoCamera.CaptureCompleted -= _PhotoCamera_CaptureCompleted;

                _PhotoCamera.Dispose();

                _PhotoCamera = null;

                System.Diagnostics.Debug.WriteLine("Camera disposed!");
            }
            
            if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary))
            {
                _podeVoltar = false;

                _PhotoCamera = new PhotoCamera(CameraType.Primary);

                _PhotoCamera.Initialized += new EventHandler<CameraOperationCompletedEventArgs>(_PhotoCamera_Initialized);

                CameraVideoBrushExtensions.SetSource(viewfinderBrush, _PhotoCamera);
            }
            else
            {
                this.Dispatcher.BeginInvoke(delegate()
                {
                    // Write message.
                    TextNotification.Text = "A Camera is not available on this device.";
                });
            }
        }

        /// <summary>
        /// Event occurs when the camera finishes its initialization.
        /// </summary>
        /// <param name="sender">PhotoCamera.</param>
        /// <param name="e">CameraOperationCompletedEventArgs.</param>
        void _PhotoCamera_Initialized(object sender, Microsoft.Devices.CameraOperationCompletedEventArgs e)
        {
            if (e.Succeeded)
            {
                if (_PhotoCamera.IsFocusSupported)
                    _PhotoCamera.AutoFocusCompleted += _PhotoCamera_AutoFocusCompleted;
                _PhotoCamera.CaptureImageAvailable += _PhotoCamera_CaptureImageAvailable;
                _PhotoCamera.CaptureCompleted += _PhotoCamera_CaptureCompleted;

                Size res;

                switch (BarcodeType)
                {
                    case BarcodeType.Qrcode:
                        //Para o Qrcode é melhor trabalhar com a menor resolução disponível
                        res = (from r in _PhotoCamera.AvailableResolutions.ToList<Size>()
                               orderby r.Height
                               select r).FirstOrDefault();
                        break;
                    case BarcodeType.Barcode:
                    default:
                        //Para o barcode é melhor trabalhar com a maior resolução disponível
                        res = (from r in _PhotoCamera.AvailableResolutions.ToList<Size>()
                               orderby r.Height
                               select r).LastOrDefault();
                        break;
                }

                if (!_PhotoCamera.Resolution.Equals(res))
                    _PhotoCamera.Resolution = res;

                _PhotoCamera.FlashMode = FlashMode.Off;

                resolutionWidth = res.Width;

                System.Diagnostics.Debug.WriteLine("Camera inicialized!");

                _BarcodeReader = new BarcodeReader();
                _BarcodeReader.Options.TryHarder = true;
                _BarcodeReader.Options.PossibleFormats = new List<BarcodeFormat>() { BarcodeFormat.ITF, BarcodeFormat.QR_CODE };
                _BarcodeReader.ResultFound += _BarcodeReader_ResultFound;
                System.Diagnostics.Debug.WriteLine("Barcode inicialized!");


                this.Dispatcher.BeginInvoke(delegate()
                {
                    _Timer = new DispatcherTimer();
                    _Timer.Interval = TimeSpan.FromMilliseconds(400);
                    _Timer.Tick += TryToFocusOrCaptureImage;
                    _Timer.Start();
                    System.Diagnostics.Debug.WriteLine("Timer inicialized!");

                    _podeVoltar = true;
                    
                    if (_barraVisivel)
                        ApplicationBar.IsVisible = true;
                });
            }
        }

        private int _TryTimes
        {
            get { return _tryTimes; }
            set
            {
                if ((value % 3) == 0)
                    ImageNotificationFromVisibleToInvisible.Begin();

                _tryTimes = value;
            }
        }

        /// <summary>
        /// Try to focus on the image and capture image.
        /// </summary>
        private void TryToFocusOrCaptureImage(object sender, EventArgs e)
        {
            try
            {
                if (_PhotoCamera.IsFocusSupported)
                    _PhotoCamera.Focus();
                else
                    _PhotoCamera.CaptureImage();

                _Timer.Stop();
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.Message);
            }
        }

        /// <summary>
        /// Occurs when the auto focus is completed.
        /// </summary>
        /// <param name="sender">PhotoCamera.</param>
        /// <param name="e">CameraOperationCompletedEventArgs.</param>
        private void _PhotoCamera_AutoFocusCompleted(object sender, CameraOperationCompletedEventArgs e)
        {
            if (e.Succeeded)
                _PhotoCamera.CaptureImage();
        }

        /// <summary>
        /// Occurs when an image captured is available.
        /// </summary>
        /// <param name="sender">PhotoCamera.</param>
        /// <param name="e">ContentReadyEventArgs.</param>
        private void _PhotoCamera_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
        {
            try
            {
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    //Get and create the image from stream.
                    BitmapImage bmp = new BitmapImage();
                    bmp.SetSource(e.ImageStream);

                    //Create the writeblebitmap to easy manipulation.
                    WriteableBitmap imageSource = new WriteableBitmap(bmp);

                    double fatorMultiplicador;
                    switch (BarcodeType)
                    {
                        case BarcodeType.Qrcode:
                            fatorMultiplicador = 1;
                            break;
                        default:
                            if (resolutionWidth >= 1024)
                                fatorMultiplicador = resolutionWidth / 1024;
                            else
                                fatorMultiplicador = 1;
                            break;
                    }

                    //**************** Calculate the x cordinate and width
                    //Get the width with margin. The margim is to avoid differents screen sizes.
                    double width = Math.Round(imageSource.PixelWidth * (Guide.ActualWidth / imageSource.PixelWidth), 0) * fatorMultiplicador;
                    //Get the x cordinate.
                    double x = Math.Round((imageSource.PixelWidth / 2.0) - (width / 2.0), 2);

                    //**************** Calculate the y cordinate and height
                    //Get the height with margim. The margim is to avoid differents screen sizes.
                    double height = Math.Round(imageSource.PixelHeight * (Guide.ActualHeight / imageSource.PixelHeight), 2) * fatorMultiplicador;
                    //Get the y cordinate.
                    double y = Math.Round((imageSource.PixelHeight / 2.0) - (height / 2.0), 2);

                    Rect cropArea = new Rect(x, y, width, height);

                    //Crop the image.
                    _WriteableBitmapCropped = WriteableBitmapExtensions.Crop(imageSource, cropArea);

                    ////Convert to gray
                    //_WriteableBitmapCropped.ForEach((a, b, color) =>
                    //{
                    //    var c = _WriteableBitmapCropped.GetPixel(a, b);

                    //    byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);
                    //    // color.A = gray;
                    //    color.R = gray;
                    //    color.G = gray;
                    //    color.B = gray;

                    //    return color;
                    //});

                    _WriteableBitmapCropped.Invalidate();

                    //Set the image cropped.
                    _BarcodeReader.Decode(_WriteableBitmapCropped);

                    switch (BarcodeType)
                    {
                        case BarcodeType.Qrcode:
                            //Set the gray image cropped to the GrayImage UIElement.
                            GrayImage.Source = _WriteableBitmapCropped.Rotate(90);
                            break;
                        default:
                            //Set the gray image cropped to the GrayImage UIElement.
                            GrayImage.Source = _WriteableBitmapCropped;
                            break;
                    }

                    //Increment the times which the image has been tried to be taken.
                    _TryTimes++;
                });
            }
            catch (Exception exception)
            {
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    Console.WriteLine(exception.Message);
                });

            }
            finally
            {
                GC.Collect();
            }
        }

        /// <summary>
        /// Occurs when the process of capturing is completed.
        /// </summary>
        /// <param name="sender">PhoneCamera.</param>
        /// <param name="e">CameraOperationCompletedEventArgs</param>
        private void _PhotoCamera_CaptureCompleted(object sender, CameraOperationCompletedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Capture completed!");

            Deployment.Current.Dispatcher.BeginInvoke(delegate()
            {
                _Timer.Start();
            });
        }

        /// <summary>
        /// Occurs when BarcodeReader finds a result.
        /// </summary>
        /// <param name="obj">Result</param>
        private void _BarcodeReader_ResultFound(Result obj)
        {
            this.Dispatcher.BeginInvoke(delegate()
            {
                VibrateController vibrateWhenCapture = VibrateController.Default;
                vibrateWhenCapture.Start(TimeSpan.FromSeconds(0.25));

                Console.WriteLine(obj.Text);
                NotififyBarcodeResultFoundChangedEventHandler(new FoundBarcodeEventArgs(_WriteableBitmapCropped, obj));
            });           
        }

        /// <summary>
        /// Barcode result event handler which is notified when the barcode or qrcode is found.
        /// </summary>
        public event EventHandler<FoundBarcodeEventArgs> BarcodeResultFoundChangedEventHandler;
        private void NotififyBarcodeResultFoundChangedEventHandler(FoundBarcodeEventArgs e)
        {
            if (BarcodeResultFoundChangedEventHandler != null)
                BarcodeResultFoundChangedEventHandler(this, e);
        }

        /// <summary>
        /// Occurs when the appbar button is clicked.
        /// </summary>
        /// <param name="sender">ApplicationBarIconButton.</param>
        /// <param name="e">EventArgs.</param>
        private void QrCodeOrBarCode_Click(object sender, EventArgs e)
        {
            this.BarcodeType = (this.BarcodeType.Equals(CameraBanking.BarcodeType.Barcode) ?
                CameraBanking.BarcodeType.Qrcode : CameraBanking.BarcodeType.Barcode);
            QrCodeOrBarCode();
        }

        private void QrCodeOrBarCode()
        {
            InitializePhotoCamera();

            switch (BarcodeType)
            {
                //Open Qrcode camera
                case BarcodeType.Qrcode:
                    if (!OverrideAppBar)
                    {
                        ApplicationBar.Buttons.Clear();
                        ApplicationBar.Buttons.Add(_BarCode);
                        if (ApplicationBar.IsVisible)
                        {
                            _barraVisivel = true;
                            ApplicationBar.IsVisible = false;
                        }
                    }

                    viewfinderTransform.ScaleX = 1;
                    viewfinderTransform.ScaleY = 1;

                    FromBarcodeToQrCodeLayout.Begin();

                    SupportedOrientations = SupportedPageOrientation.Portrait;
                    Orientation = PageOrientation.Portrait;

                    break;
                //Open Barcode camera
                case BarcodeType.Barcode:
                default:

                    if (!OverrideAppBar)
                    {
                        ApplicationBar.Buttons.Clear();
                        ApplicationBar.Buttons.Add(_QrCode);
                        //ApplicationBar.IsVisible = true;
                        _barraVisivel = true;
                    }

                    viewfinderTransform.ScaleX = 1;
                    viewfinderTransform.ScaleY = 1;

                    FromQrCodeToBarcodeLayout.Begin();

                    SupportedOrientations = SupportedPageOrientation.Landscape;
                    Orientation = PageOrientation.Landscape;

                    break;
            }

        }

        protected override void OnOrientationChanged(OrientationChangedEventArgs e)
        {
            if (SupportedOrientations == SupportedPageOrientation.Portrait)
                base.OnOrientationChanged(new OrientationChangedEventArgs(PageOrientation.PortraitDown));
            else
                base.OnOrientationChanged(new OrientationChangedEventArgs(PageOrientation.LandscapeLeft));
        }

        protected override void OnBackKeyPress(CancelEventArgs e)
        {
            if (!_podeVoltar)
                e.Cancel = true;
        }


        private bool RecuperarEstadoDaMiniatura()
        {
            string value;
            if (IsolatedStorageSettings.ApplicationSettings.TryGetValue<String>("CameraBankingHelper", out value))
            {
                if (value.Equals("true"))
                    return true;
                else
                    return false;
            }
            return true;
        }

        private void GravarEstadoDaMiniatura(bool value)
        {
            string key = "CameraBankingHelper";
            if (IsolatedStorageSettings.ApplicationSettings.Contains(key))
            {
                if (value)
                    IsolatedStorageSettings.ApplicationSettings[key] = "true";
                else
                    IsolatedStorageSettings.ApplicationSettings[key] = "false";
            }
            else
            {
                if (value)
                    IsolatedStorageSettings.ApplicationSettings.Add(key, "true");
                else
                    IsolatedStorageSettings.ApplicationSettings.Add(key, "false");
            }
            IsolatedStorageSettings.ApplicationSettings.Save();
        }
    }
}